#pragma once
/*****************************************实现文件版本的题目交互***************************** */
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
#include <cassert>
#include <fstream>
#include <cstdlib>
#include <algorithm>

#include "../comm/log.hpp"
#include "../comm/util.hpp"

namespace ns_model
{
    using namespace ns_log;
    using namespace ns_util;

    struct Question
    {
        std::string number; // 题目标题, 唯一性
        std::string title;  // 题目标题
        std::string star;   // 题目难度
        int cpulimit;       // 题目时间限制
        int memlimit;       // 题目空间限制
        std::string desc;   // 题目描述
        std::string header; // 题目预设置代码
        std::string tail;   // 题目测试代码部分
    };
    
    // 测试: ./oj_server/questions
    // static const std::string defaultpath = "./oj_server/questions/";
    // static const std::string questionlist = "./oj_server/questions/questions.list"; // 读取题目列表文件路径+名称
    static const std::string defaultpath = "./questions/";
    static const std::string questionlist = "./questions/questions.list"; // 读取题目列表文件路径+名称
    static const std::string seq = " ";
    // 该类实现题库的交互
    class Model
    {
    private:
        std::unordered_map<std::string, Question> _questions;

    private:
        /*********************
         * 功能：实现将所有题目加载成功
         * 参数：配置文件 "./questions/questions.list"
         **********************/
        bool LoadQuestionList(const std::string &file) // 加载配置文件
        {
            // 1、按行读取文件
            std::ifstream ifs(file);
            if (!ifs.is_open())
            {
                LOG(Error, "加载配置文件失败，请检查原因");
                return false;
            }
            std::string oneline;
            while (std::getline(ifs, oneline))
            {
                // 一行的内容存放在online中
                //  2、字符串解析
                //  1 回文数 简单  1 5000
                std::vector<std::string> content;
                StringUtil::SplitString(oneline, &content, seq);
                if (content.size() != 5)
                {
                    LOG(Warning, "读取题库文件: %s 失败", oneline.c_str());
                    continue;
                }
                Question q;
                q.number = content[0];
                q.title = content[1];
                q.star = content[2];
                q.cpulimit = atoi(content[3].c_str());
                q.memlimit = atoi(content[4].c_str());

                std::string path = defaultpath + q.number + "/"; // 得到对应编号的文件路径

                // 读取文件
                //std::cout << path << std::endl;
                FileUtil::ReadFile(path + "desc.txt", &(q.desc));
                FileUtil::ReadFile(path + "header.cpp", &(q.header));
                FileUtil::ReadFile(path + "tail.cpp", &(q.tail));

                _questions.insert({q.number, q});
            }
            ifs.close();
            return !ifs.bad(); //检查读取过程是否出错（排除EOF正常结束的情况）
        }

    public:
        Model()
        {
            // 在构建对象的时候，将配置好的文件加载进来
            if (LoadQuestionList(questionlist))
            {
                LOG(Info, "题库配置文件加载成功");
            }
            else
            {
                LOG(Error, "题库配置文件加载失败");
            }
        }
        ~Model()
        {
        }

    public:
        /*********************
         * 功能：将所有的题目获取出来
         *******************/
        bool GetAllQuestions(std::vector<Question> *out)
        {
            auto it = _questions.begin();
            if (it == _questions.end())
            {
                LOG(Error, "题目列表为空，没有完成加载，请检查对应的题目列表配置文件");
                return false;
            }
            else // 说明有数据
            {
                out->reserve(_questions.size());
                for (const auto &[number, question] : _questions)
                {
                    out->emplace_back(question);
                }
            }
            //对获得的数组进行题目编号的排序
            std::sort(out->begin(), out->end(), [](const Question& q1, const Question& q2){
                return std::stoi(q1.number) < std::stoi(q2.number); 
            });
            return true;
        }
        /*********************
         * 功能：将传入的单个的题目获取出来
         *******************/
        bool GetOneQuestions(const std::string &number, Question *out)
        {
            const auto &it = _questions.find(number);
            if (it == _questions.end())
            {
                LOG(Warning, "不存在访问的题目");
                return false;
            }
            else
            {
                *out = it->second;
                return true;
            }
        }
        // TODO 对题库的增删改查 —> 我们就不做了
    };

} // namespace ns_model
